﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using ifunction.JPush.V3;

namespace ifunction.JPush
{
    /// <summary>
    /// Class HttpExtensions.
    /// </summary>
    internal static class Extensions
    {
        /// <summary>
        /// Gets the string value.
        /// </summary>
        /// <param name="anyObject">Any object.</param>
        /// <param name="defaultString">The default string.</param>
        /// <returns>System.String.</returns>
        public static string GetStringValue(this string anyObject, string defaultString = "")
        {
            return !string.IsNullOrWhiteSpace(anyObject) ? anyObject : defaultString;
        }

        /// <summary>
        /// Gets the string value.
        /// </summary>
        /// <param name="anyObject">Any object.</param>
        /// <param name="defaultString">The default string.</param>
        /// <returns>System.String.</returns>
        public static string GetStringValue(this object anyObject, string defaultString = "")
        {
            return anyObject != null ? anyObject.ToString() : defaultString;
        }

        /// <summary>
        /// Creates the XML.
        /// </summary>
        /// <param name="nodeName">Name of the node.</param>
        /// <returns>XElement.</returns>
        public static XElement CreateXml(this string nodeName)
        {
            return XElement.Parse(string.Format("<{0}></{0}>", nodeName));
        }

        /// <summary>
        /// To the nullable boolean.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">if set to <c>true</c> [default value].</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool? ToNullableBoolean(this string data, bool? defaultValue = null)
        {
            bool result;
            int booleanInt;

            return
                int.TryParse(data, out booleanInt) ?
                Convert.ToBoolean(booleanInt)
                : ((data == null || !bool.TryParse(data, out result)) ? defaultValue : result);
        }

        /// <summary>
        /// Databases to string.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.String.</returns>
        public static string DBToString(this object data, string defaultValue = "")
        {
            return (data == null || data == DBNull.Value) ? defaultValue : data.ToString();
        }

        /// <summary>
        /// To GUID.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.Nullable{Guid}.</returns>
        public static Guid? DBToGuid(this object data, Guid? defaultValue = null)
        {
            Guid result;
            return (data == null || data == DBNull.Value || !Guid.TryParse(data.ToString(), out result)) ? defaultValue : result;
        }

        /// <summary>
        /// To the int32.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.Int32.</returns>
        public static int DBToInt32(this object data, int defaultValue = 0)
        {
            int result;
            if (data == null || data == DBNull.Value || !int.TryParse(data.ToString(), out result))
            {
                result = defaultValue;
            }

            return result;
        }

        /// <summary>
        /// To the int32.
        /// </summary>
        /// <param name="stringObject">The string object.</param>
        /// <returns>Int32.</returns>
        public static int? ToNullableInt32(this string stringObject, int? defaultValue = null)
        {
            Int32 result = 0;
            return Int32.TryParse(stringObject, out result) ? result : defaultValue;
        }

        /// <summary>
        /// Databases to int64.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.Int64.</returns>
        public static long DBToInt64(this object data, long defaultValue = 0)
        {
            long result;
            if (data == null || data == DBNull.Value || !long.TryParse(data.ToString(), out result))
            {
                result = defaultValue;
            }

            return result;
        }

        /// <summary>
        /// DBs to date time.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns>System.Nullable{DateTime}.</returns>
        public static DateTime? DBToDateTime(this object data)
        {
            DateTime? result = null;

            if (data != null && data != DBNull.Value)
            {
                result = data as DateTime?;
            }

            if (result != null)
            {
                result = DateTime.SpecifyKind(result.Value, DateTimeKind.Utc);
            }

            return result;
        }

        /// <summary>
        /// To the date time.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>DateTime.</returns>
        public static DateTime DBToDateTime(this object data, DateTime defaultValue)
        {
            return DBToDateTime(data) ?? defaultValue;
        }

        /// <summary>
        /// DBs to boolean.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns>The boolean value. If failed to convert, return <c>false</c>.</returns>
        public static bool DBToBoolean(this object data)
        {
            return DBToBoolean(data, false).Value;
        }

        /// <summary>
        /// To nullable boolean
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>The nullable boolean value.</returns>
        public static bool? DBToBoolean(this object data, bool? defaultValue = null)
        {
            bool result;
            string dataString = data.GetStringValue();
            int booleanInt;

            return
                int.TryParse(dataString, out booleanInt) ?
                Convert.ToBoolean(booleanInt)
                : ((data == null || data == DBNull.Value || !bool.TryParse(dataString, out result)) ? defaultValue : result);
        }

        /// <summary>
        /// To the nullable int32.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>System.Nullable{System.Int32}.</returns>
        public static int? DBToNullableInt32(this object data, int? defaultValue = null)
        {
            int result;
            return (data == null || data == DBNull.Value || !int.TryParse(data.ToString(), out result)) ? defaultValue : result;
        }

        /// <summary>
        /// Enums to int32.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="enumValue">The enum value.</param>
        /// <returns>System.Nullable{System.Int32}.</returns>
        public static int? EnumToInt32<T>(this T? enumValue) where T : struct,IConvertible
        {
            int? result = null;
            if (enumValue != null)
            {
                IConvertible convertible = enumValue.Value;
                result = convertible.ToInt32(CultureInfo.InvariantCulture);
            }

            return result;
        }

        /// <summary>
        /// Gets the attribute value.
        /// </summary>
        /// <param name="xElement">The x element.</param>
        /// <param name="attribute">The attribute.</param>
        /// <returns>System.String.</returns>
        public static string GetAttributeValue(this XElement xElement, string attribute)
        {
            string result = string.Empty;

            if (xElement != null && !string.IsNullOrWhiteSpace(attribute))
            {
                var attr = xElement.Attribute(attribute);
                if (attr != null)
                {
                    result = attr.Value;
                }
            }

            return result;
        }


        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="V"></typeparam>
        /// <param name="dictionary">The dictionary.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml<T, V>(this Dictionary<T, V> dictionary)
        {
            SerializableDictionary<T, V> result = null;

            if (dictionary != null)
            {
                result = new SerializableDictionary<T, V>(dictionary);
            }

            return result == null ? null : result.ToXml();
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="audience">The audience.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this Audience audience)
        {
            SerializableDictionary<PushTypeV3, List<string>> dictionary = new SerializableDictionary<PushTypeV3, List<string>>(audience);

            return dictionary.ToXml();
        }

        /// <summary>
        /// Converts pure XML to audience.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>Audience.</returns>
        public static Audience PureXmlToAudience(this XElement xml)
        {
            SerializableDictionary<PushTypeV3, List<string>> dictionary = new SerializableDictionary<PushTypeV3, List<string>>();

            dictionary.LoadFromXml(xml);

            return new Audience(dictionary);
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="audience">The audience.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this iOSNotificationParameters notificationParameters)
        {
            XElement xml = null;

            if (notificationParameters != null)
            {
                xml = "NotificationParameter".CreateXml();

                xml.SetAttributeValue("Alert", notificationParameters.Alert);
                xml.SetAttributeValue("Badge", notificationParameters.Badge);
                xml.SetAttributeValue("SilenceMode", notificationParameters.SilenceMode);
                xml.SetAttributeValue("Sound", notificationParameters.Sound);
                xml.Add(notificationParameters.CustomizedValues.ToPureXml());
            }

            return xml;
        }

        /// <summary>
        /// Convert from pure XML to notification parameter
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>iOSNotificationParameters.</returns>
        public static iOSNotificationParameters PureXmlToiOSNotificationParameters(this XElement xml)
        {
            iOSNotificationParameters result = new iOSNotificationParameters();

            if (xml != null)
            {
                result.Alert = xml.GetAttributeValue("Alert");
                result.Badge = xml.GetAttributeValue("Badge").DBToString();
                result.SilenceMode = xml.GetAttributeValue("SilenceMode").ToNullableBoolean();
                result.Sound = xml.GetAttributeValue("Sound");

                var dictionary = new SerializableDictionary<string, string>();
                dictionary.LoadFromXml(xml.Elements().FirstOrDefault());
                result.CustomizedValues = dictionary;
            }

            return result;
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="notificationParameters">The notification parameters.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this AndroidNotificationParameters notificationParameters)
        {
            XElement xml = null;

            if (notificationParameters != null)
            {
                xml = "NotificationParameter".CreateXml();

                xml.SetAttributeValue("Alert", notificationParameters.Alert);
                xml.SetAttributeValue("BuilderId", notificationParameters.BuilderId);
                xml.SetAttributeValue("Title", notificationParameters.Title);
                xml.Add(notificationParameters.CustomizedValues.ToPureXml());
            }

            return xml;
        }

        /// <summary>
        /// Convert from pure XML to notification parameter
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>AndroidNotificationParameters.</returns>
        public static AndroidNotificationParameters PureXmlToAndroidNotificationParameters(this XElement xml)
        {
            AndroidNotificationParameters result = new AndroidNotificationParameters();

            if (xml != null)
            {
                result.Alert = xml.GetAttributeValue("Alert");
                result.BuilderId = xml.GetAttributeValue("BuilderId").ToNullableInt32();
                result.Title = xml.GetAttributeValue("Title");

                var dictionary = new SerializableDictionary<string, string>();
                dictionary.LoadFromXml(xml.Elements().FirstOrDefault());
                result.CustomizedValues = dictionary;
            }

            return result;
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="notificationParameters">The notification parameters.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this WindowsPhoneNotificationParameters notificationParameters)
        {
            XElement xml = null;

            if (notificationParameters != null)
            {
                xml = "NotificationParameter".CreateXml();

                xml.SetAttributeValue("Alert", notificationParameters.Alert);
                xml.SetAttributeValue("OpenPage", notificationParameters.OpenPage);
                xml.SetAttributeValue("Title", notificationParameters.Title);
                xml.Add(notificationParameters.CustomizedValues.ToPureXml());
            }

            return xml;
        }

        /// <summary>
        /// Convert from pure XML to notification parameter
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>WindowsPhoneNotificationParameters.</returns>
        public static WindowsPhoneNotificationParameters PureXmlToWindowsPhoneNotificationParameters(this XElement xml)
        {
            var result = new WindowsPhoneNotificationParameters();

            if (xml != null)
            {
                result.Alert = xml.GetAttributeValue("Alert");
                result.OpenPage = xml.GetAttributeValue("OpenPage");
                result.Title = xml.GetAttributeValue("Title");

                var dictionary = new SerializableDictionary<string, string>();
                dictionary.LoadFromXml(xml.Elements().FirstOrDefault());
                result.CustomizedValues = dictionary;
            }

            return result;
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="notification">The notification.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this Notification notification)
        {
            XElement xml = null;

            if (notification != null)
            {
                xml = "Notification".CreateXml();

                var iosXml = notification.iOSNotification.ToPureXml();
                var androidXml = notification.AndroidNotification.ToPureXml();
                var windowsPhoneXml = notification.WindowsPhoneNotification.ToPureXml();

                if (iosXml != null)
                {
                    iosXml.SetAttributeValue("type", "ios");
                    xml.Add(iosXml);
                }

                if (androidXml != null)
                {
                    androidXml.SetAttributeValue("type", "android");
                    xml.Add(androidXml);
                }

                if (windowsPhoneXml != null)
                {
                    windowsPhoneXml.SetAttributeValue("type", "wp");
                    xml.Add(windowsPhoneXml);
                }
            }

            return xml;
        }

        /// <summary>
        /// Converts from XML to notification.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>Notification.</returns>
        public static Notification PureXmlToNotification(this XElement xml)
        {
            Notification result = null;

            if (xml != null)
            {
                result = new Notification();

                foreach (var one in xml.Elements())
                {
                    switch (one.GetAttributeValue("type"))
                    {
                        case "ios":
                            result.iOSNotification = one.PureXmlToiOSNotificationParameters();
                            break;
                        case "android":
                            result.AndroidNotification = one.PureXmlToAndroidNotificationParameters();
                            break;
                        case "wp":
                            result.WindowsPhoneNotification = one.PureXmlToWindowsPhoneNotificationParameters();
                            break;
                        default:
                            break;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// To the pure XML.
        /// </summary>
        /// <param name="appMessage">The application message.</param>
        /// <returns>XElement.</returns>
        public static XElement ToPureXml(this AppMessage appMessage)
        {
            XElement xml = "AppMessage".CreateXml();

            if (appMessage != null)
            {
                xml.SetAttributeValue("Content", appMessage.Content);
                xml.SetAttributeValue("ContentType", appMessage.ContentType);
                xml.SetAttributeValue("Title", appMessage.Title);
                xml.Add(appMessage.CustomizedValue.ToPureXml());
            }

            return xml;
        }

        /// <summary>
        /// Pures the XML to application message.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns>AppMessage.</returns>
        public static AppMessage PureXmlToAppMessage(this XElement xml)
        {
            AppMessage result = null;

            if (xml != null)
            {
                result = new AppMessage
                {
                    Content = xml.GetAttributeValue("Content"),
                    ContentType = xml.GetAttributeValue("ContentType"),
                    Title = xml.GetAttributeValue("Title")
                };

                var dictionary = new SerializableDictionary<string, string>();
                dictionary.LoadFromXml(xml.Elements().FirstOrDefault());
                result.CustomizedValue = dictionary;
            }

            return result;
        }
    }
}
